package com.pet.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pet.api.client.CartClient;
import com.pet.api.client.ItemClient;
import com.pet.api.client.UserClient;
import com.pet.api.dto.AddressDTO;
import com.pet.api.dto.ItemDTO;
import com.pet.api.dto.OrderDetailDTO;
import com.pet.common.domain.PageDTO;
import com.pet.common.domain.Result;
import com.pet.common.domain.UserContext;
import com.pet.common.exception.BusinessException;
import com.pet.order.domain.dto.OrderCheckDTO;
import com.pet.order.domain.dto.PayFormDTO;
import com.pet.order.domain.dto.RefundFormDTO;
import com.pet.order.domain.po.Order;
import com.pet.order.domain.po.OrderDetail;
import com.pet.order.domain.po.OrderLogistics;
import com.pet.order.domain.query.OrderQuery;
import com.pet.order.domain.vo.*;
import com.pet.order.mapper.OrderMapper;
import com.pet.order.service.IOrderDetailService;
import com.pet.order.service.IOrderLogisticsService;
import com.pet.order.service.IOrderService;
import com.pet.order.utils.RedisIdWorker;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.pet.common.utils.RabbitConstants.ORDER_CHECK_EXCHANGE;
import static com.pet.common.utils.RabbitConstants.ORDER_CHECK_KEY;
import static com.pet.common.utils.RedisConstants.LOCK_ORDER_KEY;
import static com.pet.common.utils.RedisConstants.LOCK_ORDER_TTL;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author Cookies
 * @since 2024-07-11
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
    private final IOrderDetailService orderDetailService;
    private final IOrderLogisticsService orderLogisticsService;
    private final RedisIdWorker redisIdWorker;
    private final RabbitTemplate rabbitTemplate;
    private final ItemClient itemClient;
    private final CartClient cartClient;
    private final UserClient userClient;
    private final RedissonClient redissonClient;

    @Override
    @GlobalTransactional
    public Result createOrder(List<OrderDetailDTO> detailDTOList) {
        Map<Integer, Integer> itemNumMap = detailDTOList.stream()
                .collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
        Set<Integer> itemIds = itemNumMap.keySet();
        if (itemIds.isEmpty()) {
            return Result.error("请选择商品");
        }
        // 获取当前操作用户
        Integer userId = UserContext.getUser();
        // 加锁, 防止用户多次点击重复下单
        RLock lock = redissonClient.getLock(LOCK_ORDER_KEY + userId);
        boolean isLock;
        try {
            isLock = lock.tryLock(1, LOCK_ORDER_TTL, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return Result.error("操作失败, 请联系管理员处理~");
        }
        // 获取锁失败逻辑
        if (!isLock) {
            return Result.error("操作太频繁啦, 请稍后再试吧~");
        }
        try {
            // 查询商品
            List<ItemDTO> items = itemClient.getItemsByIds(itemIds);
            if (items == null || items.size() < itemIds.size()) {
                return Result.error("商品不存在");
            }
            int total = 0;
            for (ItemDTO item : items) {
                total += item.getPrice() * itemNumMap.get(item.getId());
            }
            Integer discount = userClient.queryDiscountByUser();
            total = total * discount / 100;
            // 保存订单
            long orderId = redisIdWorker.nextId("order");
            Order order = new Order();
            order.setId(orderId);
            order.setTotalFee(total);
            order.setDiscountRate(discount);
            order.setUserId(userId);
            save(order);

            // 保存订单详情
            List<OrderDetail> details = buildDetails(orderId, items, itemNumMap);
            orderDetailService.saveBatch(details);

            // 清理购物车商品
            cartClient.removeItemsByIds(itemIds);

            // 扣减库存
            try {
                itemClient.deductStock(detailDTOList);
            } catch (Exception e) {
                throw new BusinessException("库存不足");
            }

            // 设置订单超时检测
            OrderCheckDTO orderCheckDTO = new OrderCheckDTO(orderId, userId);
            rabbitTemplate.convertAndSend(ORDER_CHECK_EXCHANGE, ORDER_CHECK_KEY, orderCheckDTO, message -> {
                message.getMessageProperties().setDelay(1000 * 60 * 15);
                return message;
            });
            return Result.success(orderId);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Result cancelOrder(Long orderId) {
        if (orderId == null) {
            return Result.error("订单不存在");
        }
        Integer userId = UserContext.getUser();
        Order order = lambdaQuery()
                .eq(Order::getId, orderId)
                .eq(Order::getUserId, userId)
                .one();
        if (order == null) {
            return Result.error("对应用户的订单不存在");
        }
        Integer status = order.getStatus();
        if (status > 1) {
            return Result.error("当前不支持取消该订单");
        } else if (status == 1) {
            // 处理退单
            handleRefundOrder(orderId, order.getTotalFee());
        }
        handleCancelOrder(orderId);
        return Result.success();
    }

    @Override
    public void timeout2CheckOrder(OrderCheckDTO orderCheckDTO) {
        long orderId = orderCheckDTO.getOrderId();
        if (orderId == 0L) {
            return;
        }
        Order order = lambdaQuery()
                .eq(Order::getId, orderId)
                .one();
        if (order == null || order.getStatus() > 1) {
            return;
        }
        UserContext.setUser(orderCheckDTO.getUserId());
        handleCancelOrder(orderId);
    }

    @Override
    public Result getUnpaidOrderDetail(Long orderId) {
        Integer userId = UserContext.getUser();
        Order order = lambdaQuery()
                .eq(Order::getId, orderId)
                .eq(Order::getUserId, userId)
                .one();
        if (order == null || order.getStatus() > 1) {
            return Result.error("订单不存在或已支付");
        }
        List<OrderDetail> detailList = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderId)
                .list();
        List<OrderItemDetailVO> itemDetailVOList = new ArrayList<>(detailList.size());
        detailList.stream()
                .forEach(detail -> {
                    OrderItemDetailVO detailVO = BeanUtil.copyProperties(detail, OrderItemDetailVO.class);
                    detailVO.setPrice(detailVO.getPrice() * order.getDiscountRate() / 100);
                    itemDetailVOList.add(detailVO);
                });
        OrderDetailVO detailVO = BeanUtil.copyProperties(order, OrderDetailVO.class);
        detailVO.setItems(itemDetailVOList);
        return Result.success(detailVO);
    }

    @Override
    @GlobalTransactional
    public Result payOrder(PayFormDTO payFormDTO) {
        Integer userId = UserContext.getUser();
        Long orderId = payFormDTO.getOrderId();
        Order order = lambdaQuery()
                .eq(Order::getId, orderId)
                .eq(Order::getUserId, userId)
                .one();
        if (order == null || order.getStatus() > 0) {
            return Result.error("订单不存在或已支付");
        }

        // 获取收货地址并保存
        AddressDTO addressDTO = userClient.queryAddressById(payFormDTO.getAddressId());
        OrderLogistics orderLogistics = BeanUtil.copyProperties(addressDTO, OrderLogistics.class);
        orderLogistics.setOrderId(orderId);
        orderLogisticsService.save(orderLogistics);

        // 增加用户积分
        userClient.addPointsByPay(order.getTotalFee());

        // 更新支付类型/支付状态/支付时间
        boolean isSuccess = lambdaUpdate()
                .set(Order::getPaymentType, payFormDTO.getPaymentType())
                .set(Order::getStatus, 1)
                .set(Order::getPaymentTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .eq(Order::getUserId, userId)
                .update();
        if (!isSuccess) {
            log.error("修改订单'{}'支付状态失败", orderId);
            throw new BusinessException("订单支付状态修改失败");
        }
        return Result.success();
    }

    @Override
    public Result refundOrder(RefundFormDTO refundFormDTO) {
        // 校验订单信息
        Integer userId = UserContext.getUser();
        Long orderId = refundFormDTO.getOrderId();
        Order order = lambdaQuery()
                .eq(Order::getUserId, userId)
                .eq(Order::getId, orderId)
                .one();
        if (order == null) {
            return Result.error("订单不存在");
        }
        if (order.getStatus() != 2 && order.getStatus() != 3) {
            return Result.error("当前订单无法申请退单");
        }

        // 修改订单状态
        boolean isSuccess = lambdaUpdate()
                .set(Order::getStatus, -2)
                .set(Order::getRefundRequestTime, LocalDateTime.now())
                .set(Order::getRefundReason, refundFormDTO.getRefundReason())
                .eq(Order::getUserId, userId)
                .eq(Order::getId, orderId)
                .update();
        if (!isSuccess) {
            log.error("修改订单'{}'退款申请状态失败", orderId);
            throw new BusinessException("订单退款申请状态修改失败");
        }
        return Result.success();
    }

    @Override
    public Result getOrderListByCategory(Integer type) {
        Integer userId = UserContext.getUser();
        // 根据类别筛选订单列表
        List<Order> orderList = lambdaQuery()
                .eq(Order::getUserId, userId)
                .eq(type != 9, Order::getStatus, type)
                .orderByDesc(Order::getCreatedAt)
                .list();
        if (orderList == null || orderList.isEmpty()) {
            return Result.success(Collections.emptyList());
        }
        // 转换视图对象
        List<OrderVO> orderVOList = BeanUtil.copyToList(orderList, OrderVO.class);
        // 获取订单id列表
        List<Long> orderIds = orderList.stream()
                .map(Order::getId)
                .collect(Collectors.toList());
        // 映射orderId与对应的商品详情
        Map<Long, List<OrderItemDetailVO>> orderDetailMap = orderDetailService.lambdaQuery()
                .in(OrderDetail::getOrderId, orderIds)
                .list().stream()
                .map(orderDetail -> BeanUtil.copyProperties(orderDetail, OrderItemDetailVO.class))
                .collect(Collectors.groupingBy(OrderItemDetailVO::getOrderId));
        // 赋值给对应的订单视图对象
        orderVOList.stream().forEach(orderVO -> orderVO.setDetails(orderDetailMap.get(orderVO.getId())));
        return Result.success(orderVOList);
    }

    @Override
    public Result getOrderListByAdmin(OrderQuery orderQuery) {
        Integer pageNo = orderQuery.getPageNo();
        Integer pageSize = orderQuery.getPageSize();
        Long orderId = orderQuery.getOrderId();
        // 搜索条件检索分页查询
        Page<Order> orderPage = lambdaQuery()
                .eq(orderId != null, Order::getId, orderId)
                .page(new Page<>(pageNo, pageSize));
        // 获取用户信息集合
        Set<Integer> userIds = orderPage.getRecords().stream()
                .map(Order::getUserId)
                .collect(Collectors.toSet());
        Map<Integer, String> idNicknameMap = userClient.getIdNicknameMap(userIds);
        // 解析订单信息
        PageDTO<OrderBriefVO> orderBriefVOPage = PageDTO.of(orderPage, order -> {
            OrderBriefVO orderBriefVO = BeanUtil.copyProperties(order, OrderBriefVO.class);
            orderBriefVO.setNickname(idNicknameMap.get(order.getUserId()));
            return orderBriefVO;
        });
        return Result.success(orderBriefVOPage);
    }

    @Override
    public Result getOrderById(Long id) {
        Order order = lambdaQuery()
                .eq(Order::getId, id)
                .one();
        return Result.success(BeanUtil.copyProperties(order, OrderInfoVO.class));
    }

    @Override
    public Result shipOrder(Long id) {
        boolean isSuccess = lambdaUpdate()
                .set(Order::getStatus, 2)
                .set(Order::getShipTime, LocalDateTime.now())
                .eq(Order::getId, id)
                .eq(Order::getStatus, 1)
                .update();
        return isSuccess ? Result.success() : Result.error("处理订单发货失败");
    }

    @Override
    public Result auditRefund(Long orderId, Boolean result) {
        // 判断订单是否存在
        Order order = lambdaQuery()
                .eq(Order::getId, orderId)
                .one();
        if (order == null) {
            return Result.error("订单不存在");
        }
        if (order.getStatus() != -2 || order.getStatus() == 4) {
            return Result.error("用户未发起退单或订单已结束");
        }
        boolean isTrue = BooleanUtil.isTrue(result);
        LocalDateTime now = LocalDateTime.now();
        boolean isSuccess;
        if (isTrue) {
            // 审核通过结果保存
            isSuccess = lambdaUpdate()
                    .set(Order::getStatus, -1)
                    .set(Order::getRefundAuditResult, 1)
                    .set(Order::getRefundAuditTime, now)
                    .set(Order::getCancelTime, now)
                    .eq(Order::getId, orderId)
                    .eq(Order::getStatus, -2)
                    .update();

            // 处理退单
            handleRefundOrder(orderId, order.getTotalFee());
        } else {
            // 审核未通过回退状态
            Integer status = null;
            if (order.getReceiveTime() != null) {
                status = 3;
            } else if (order.getShipTime() != null) {
                status = 2;
            }
            if (status == null) {
                return Result.error("订单状态异常");
            }
            // 审核未通过结果保存
            isSuccess = lambdaUpdate()
                    .set(Order::getStatus, status)
                    .set(Order::getRefundAuditResult, 0)
                    .set(Order::getRefundAuditTime, now)
                    .eq(Order::getId, orderId)
                    .eq(Order::getStatus, -2)
                    .update();
        }
        return isSuccess ? Result.success() : Result.error("处理订单失败");
    }

    @Override
    public Result confirmShip(Long orderId) {
        Integer userId = UserContext.getUser();
        boolean isSuccess = lambdaUpdate()
                .set(Order::getStatus, 3)
                .set(Order::getReceiveTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .eq(Order::getUserId, userId)
                .eq(Order::getStatus, 2)
                .update();
        return isSuccess ? Result.success() : Result.error("订单收货失败");
    }

    @Override
    public void updateOrderByReview(Long orderId) {
        Integer userId = UserContext.getUser();
        boolean isSuccess = lambdaUpdate()
                .set(Order::getStatus, 4)
                .set(Order::getEvaluateTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .eq(Order::getUserId, userId)
                .eq(Order::getStatus, 3)
                .update();
        if (!isSuccess) {
            log.error("用户评价后修改订单状态失败, 订单id: {}", orderId);
            throw new BusinessException("订单状态修改失败");
        }
    }

    @Override
    public List<Integer> getDetailByOrder(Long orderId) {
        List<OrderDetail> details = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderId)
                .list();
        if (details == null || details.isEmpty()) {
            throw new BusinessException("订单不存在");
        }
        List<Integer> itemIds = details.stream()
                .map(OrderDetail::getItemId)
                .collect(Collectors.toList());
        return itemIds;
    }

    @GlobalTransactional
    private void handleRefundOrder(Long orderId, Integer totalFee) {
        // 获取订单商品
        List<OrderDetail> orderDetailList = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderId)
                .list();
        List<OrderDetailDTO> detailDTOList = orderDetailList.stream()
                .map(orderDetail -> new OrderDetailDTO(orderDetail.getItemId(), orderDetail.getNumber()))
                .collect(Collectors.toList());

        // 恢复库存
        itemClient.appendStock(detailDTOList);

        // 扣减积分
        userClient.addPointsByPay(-totalFee);
    }

    @GlobalTransactional
    private void handleCancelOrder(Long orderId) {
        // 获取订单商品
        List<OrderDetail> orderDetailList = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, orderId)
                .list();
        List<OrderDetailDTO> detailDTOList = orderDetailList.stream()
                .map(orderDetail -> new OrderDetailDTO(orderDetail.getItemId(), orderDetail.getNumber()))
                .collect(Collectors.toList());

        // 修改订单状态
        boolean isSuccess = lambdaUpdate()
                .set(Order::getStatus, -1)
                .set(Order::getCancelTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .update();
        if (!isSuccess) {
            log.error("修改订单'{}'状态失败", orderId);
            throw new BusinessException("订单状态修改失败");
        }

        try {
            // 商品加回购物车
            cartClient.addItemsById(detailDTOList);
            // 回退库存
            itemClient.appendStock(detailDTOList);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
    }

    private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Integer, Integer> numMap) {
        List<OrderDetail> details = new ArrayList<>(items.size());
        for (ItemDTO item : items) {
            OrderDetail detail = new OrderDetail();
            detail.setOrderId(orderId);
            detail.setItemId(item.getId());
            detail.setNumber(numMap.get(item.getId()));
            detail.setTitle(item.getTitle());
            detail.setPrice(item.getPrice());
            detail.setImage(item.getImage());
            details.add(detail);
        }
        return details;
    }
}
